#include "moveGenerator.h"

MoveGenerator::MoveGenerator() {
}

// ----------
// generuje vsechny pripustne tahy pro zadaneho hrace ze urciteho stavu na sachovnici
// aPlayer			hrac, pro ktereho se maji tahy generovat (BLACK || WHITE)
// aBoard 			stat hraci plochy ,ze ktereho se maji tahy generovat
// aStorage			ukazatel na strukturu typu Moves do ktere se ukladaji vygenerovane tahy
void MoveGenerator::Generate(CheckerColor aPlayer, Board aBoard, Moves* aStorage) {
	// ---------
	// inicializace
	this->player = aPlayer;
	this->opponent = (aPlayer == WHITE) ? BLACK : WHITE;

	this->currentBoard = aBoard;
	this->movesStorage = aStorage;

	// ----------
	// generovani tahu
	this->generateNormalMoves();
	this->generateQueenMoves();
}


// ----------
// generuje tahy pro bile obycejne kameny
void MoveGenerator::generateNormalMoves() {
	bool result = false;
	bitboard occupied = this->currentBoard.NormalCheckers | this->currentBoard.Queens;

	// najde vsechny obycekne kameny
	bitboard movesFrom = this->currentBoard.NormalCheckers & this->currentBoard.Color[this->player];

	// pro kazdy kamen generuje tahy
	int positionFrom = 0;
	while(BitboardUtils::BitScanForward(movesFrom, &positionFrom)) {
		BitboardUtils::TestBitAndReset(&movesFrom, positionFrom);

		// ----------
		// generuje normalni tahy pro obycejne kameny

		// najde pole, kam kamen muze tahnout
		bitboard movesTo = 0;
		if(this->player == BLACK) {
			// pro bile (pohyb pouze smerem dolu na neobsazene pole)
			movesTo = (MovesMasks[2][positionFrom] | MovesMasks[3][positionFrom]) & ~occupied;
		}
		else {
			// pro cerne (pohyb pouze smerem nahoru na neobsazene pole)
			movesTo = (MovesMasks[0][positionFrom] | MovesMasks[1][positionFrom]) & ~occupied;
		}
		// pro vsechna cilova pole vygeneruje tah a ulozi ho
		int positionTo = 0;
		while(BitboardUtils::BitScanForward(movesTo, &positionTo)) {
			BitboardUtils::TestBitAndReset(&movesTo, positionTo);
			addMove(positionFrom, positionTo);
		}


		// ----------
		// generuje preskoky pro obycejne kameny
				Move tempStorage;
				// vytvorime novy rozpracovany tah
				tempStorage.Player = this->player;
				tempStorage.BeforeMove = this->currentBoard;

				tempStorage.ToCount = 0;
				tempStorage.CapturedCount = 0;
				
				tempStorage.From = positionFrom;
		

		if(this->player == BLACK) {
			// bile kameny se mohou pohybovat pouze dolu
			bool directions[4] = {false, false, true, true};
			this->generateJumpsFrom(positionFrom, directions, &tempStorage);
		}
		else {
			// cerne kameny se mohou pohybovat pouze nahoru
			bool directions[4] = {true, true, false, false};
			this->generateJumpsFrom(positionFrom, directions, &tempStorage);
		}
	}
}

// ----------
// funkce ktera generuje preskoky pro kamen z pole "from" v povolenych smerech

// int from							cislo pole odkud se kamen pohybuje
// bool directions[4]		pole povolenych smenu [nahoru doleva, nahoru doprava, dolu doprava, dolu doleva]
// Move* tempStorage		docasna struktura ve ktere se uchovava "rozpracovany" tah

// vraci "true" pokud se podarilo vygenerovat nejaky tah, jinak vraci "false"
bool MoveGenerator::generateJumpsFrom(int from, bool directions[4], Move* tempStorage) {
	bool result = false;
	bitboard occupied = this->currentBoard.NormalCheckers | this->currentBoard.Queens;

	for(int direction = 0; direction < 4; direction++) {
		// pokud smer neni povoleny pokracujeme
		if(directions[direction] == false)
			continue;

		// zjistime jestli muzeme preskok v danem smeru provest
		// (MovesMasks[direction][from] & this->currentBoard.Color[this->opponent]) - pole pred kamenem v danem smeru je obsazeno souperem
		//																																						(pole pred kamenem zjistime jako pole na ktere by se kamen posunul pri normalnim tahu)
		//  (JumpsMasks[direction][from] & ~occupied)																- pole na ktere preskakujeme musi byt prazdne																																					
		if((MovesMasks[direction][from] & this->currentBoard.Color[this->opponent]) && (JumpsMasks[direction][from] & ~occupied)) {
			result = true;

			int positionTo = 0;
			BitboardUtils::BitScanForward(JumpsMasks[direction][from], &positionTo);

			int positionCaptured = 0;
			BitboardUtils::BitScanForward(MovesMasks[direction][from], &positionCaptured);


			
			// ulozime prave vygenerovany pohyb
			tempStorage->To[tempStorage->ToCount] = positionTo;
			tempStorage->ToCount++;

			tempStorage->Captured[tempStorage->CapturedCount] = positionCaptured;
			tempStorage->CapturedCount++;
			
			this->addMove(tempStorage);

			//pokusime se vygenerovat dalsi preskok z pozice, kam jsme se dostali
			this->generateJumpsFrom(positionTo, directions, tempStorage);
		}
	}
	return result;
}

// -----------
// generuje tahy pro damy
void MoveGenerator::generateQueenMoves() {
  // najdeme vsechny damy pro daneho hrace
	bitboard movesFrom = this->currentBoard.Color[this->player] & this->currentBoard.Queens;
	int positionFrom = 0;
	
	// pro kazdou z nich generujeme tahy
	while(BitboardUtils::BitScanForward(movesFrom, &positionFrom)) {
		BitboardUtils::TestBitAndReset(&movesFrom, positionFrom);
		
		this->generateQueenMovesFrom(positionFrom);
	}
}

// ----------
// funkce, ktera generuje preskoky pro damu z pole from v povolenych smerech

// from									cislo pole, odkud se dama pohybuje
// bool directions[4]		pole povolenych smenu [nahoru doleva, nahoru doprava, dolu doprava, dolu doleva]
// Move tempStorage			docasna struktura ve ktere se uchovava "rozpracovany" tah
void MoveGenerator::generateQueenJumpsFrom(int from, bool directions[4], Move tempStorage) {
	// kopie hraci plochy
	// pri generovani tahu pro damu (nekolikanasobne preskoky) je nutne dany nekolikanasobny tah postupne provedet
	// protoze muze nastat pripad, kdy dama skace pres pole na kterm puvodne stala, nebo pres pole na kterem stala
	// nektera vyhozena souperova figura. To u normalnich kamenu nemuze nastat.
	Board tempBoard;
	tempBoard = this->currentBoard;
	// provedeme rozpracovany tah
	tempBoard.DoMove(tempStorage);
	
	bitboard occupied = tempBoard.NormalCheckers | tempBoard.Queens;

	for(int direction = 0; direction < 4; direction++) {
		// pokud smer neni povoleny preskocime na dalsi
		if(directions[direction] == false)
			continue;

		int positionFrom = from;

		bitboard movesTo = MovesMasks[direction][positionFrom] & ~occupied;
		int positionTo = -1;
		
		// generuje mozne pohyby pred preskokem souperova kamene
		while(BitboardUtils::BitScanForward(movesTo, &positionTo)) {
			positionFrom = positionTo;
			
			movesTo = MovesMasks[direction][positionFrom] & ~occupied;
		}

		// generuje preskok souperova kamene, pokud je to mozne
		if((MovesMasks[direction][positionFrom] & tempBoard.Color[this->opponent]) && (JumpsMasks[direction][positionFrom] & ~occupied)) {
			BitboardUtils::BitScanForward(JumpsMasks[direction][positionFrom], &positionTo);

			// zjistime pole, ze ktereho zajimame souperuv kamen
			int positionCaptured = -1;
			BitboardUtils::BitScanForward(MovesMasks[direction][positionFrom], &positionCaptured);
			
			// castecne ulozime provedeny skok, zbytek se provede az v cyklu do{} while()
			tempStorage.ToCount++;

			tempStorage.Captured[tempStorage.CapturedCount] = positionCaptured;
			tempStorage.CapturedCount++;
			
			bitboard movesTo = 0;

			do {
				// ulozime tah
				tempStorage.To[tempStorage.ToCount-1] = positionTo;
				this->addMove(&tempStorage);

				// generovani dalsich preskoku ze soucasne pozice
				bool dirs[4] = {true, true, true, true};
				// muzeme se pohybovat vsemy smery mimo toho, ze ktereho jsme prisli
				switch(direction) {
					case 0: dirs[2] = false; break;
					case 1: dirs[3] = false; break;
					case 2: dirs[0] = false; break;
					case 3: dirs[1] = false; break;
				}

				this->generateQueenJumpsFrom(positionTo, dirs, tempStorage);
				
				// generuje dalsi pohyb po preskoku souperova kamene (dama se muze zastavin na jakemkoli poli za souperovym
				// kamenem
				positionFrom = positionTo;
				movesTo = MovesMasks[direction][positionFrom] & ~occupied;

			} while(BitboardUtils::BitScanForward(movesTo, &positionTo));
		}
	}
}

// ----------
// funkce, ktera generuje tahy pro damu z pole from
void MoveGenerator::generateQueenMovesFrom(int from) {
	bitboard occupied = this->currentBoard.NormalCheckers | this->currentBoard.Queens;

	// tahy generujeme ve vsech 4 moznych smerech
	for(int direction = 0; direction < 4; direction++) {
		Move* lastMove = 0;
		int positionFrom = from;

		bitboard movesTo = MovesMasks[direction][from] & ~occupied;
		int positionTo = -1;
		
		// generuje mozne tahy v danem smeru dokud nenarazime na souperuv kamen, nebo konec hraci plochy
		while(BitboardUtils::BitScanForward(movesTo, &positionTo)) {
			lastMove = this->addMove(from, positionTo);
			positionFrom = positionTo;
			
			movesTo = MovesMasks[direction][positionFrom] & ~occupied;
		}

		// z pozice, kam jsme se dostali se pokusime vygenerovat preskok souperovy figury v danem smeru
		// preskok generujeme pouze ve smeru, ve kterem jsme provadeli predchozi pohyb
		bool dirs[4] = {false, false, false, false};
		dirs[direction] = true;

		Move tempStorage;
		tempStorage.Player = this->player;
		tempStorage.BeforeMove = this->currentBoard;
		tempStorage.ToCount = 0;
		tempStorage.CapturedCount = 0;
		tempStorage.From = from;
				
		// generujeme preskoky
		this->generateQueenJumpsFrom(positionFrom, dirs, tempStorage);
	}
}

// vygeneruje a ulozi jednoduchy tah
Move* MoveGenerator::addMove(int from, int to) {
	int index = this->movesStorage->MovesCount;

	this->movesStorage->MovesList[index].Player = this->player;
	this->movesStorage->MovesList[index].BeforeMove = this->currentBoard;
	this->movesStorage->MovesList[index].From = from;
	this->movesStorage->MovesList[index].To[0] = to;
	this->movesStorage->MovesList[index].ToCount = 1;
	this->movesStorage->MovesList[index].CapturedCount = 0;

	this->movesStorage->MovesCount++;

	return &this->movesStorage->MovesList[index];
}

// ulozi tah
void MoveGenerator::addMove(Move* temp) {
	int index = this->movesStorage->MovesCount;

	this->movesStorage->MovesList[index] = *temp;

	this->movesStorage->MovesCount++;
}